/**
 * author:Administrator
 * date:2016/1/4
 * desc:TODO
 */

'use strict';

var assert = require('assert');
var daoCU = require('../dao/couponUser.js');
var daoCV = require('../dao/couponVendor.js');
var daoTC = require('../dao/transCoupon.js');
var daoCnt = require('../dao/counter.js');

var biz = {};
module.exports = biz;

/**
 * 用户申请coupon
 *
 ** mongodb 事务  two phase commits
 ** refer : https://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/
 *
 * 正常流程 7步
 * edit步骤中，必须记录响应的transid
 *
 *      docA    docB    trans
 *      -       -       initial
 *      -       -       pending
 *      edit    -       -
 *      -       edit    -
 *      -       -       applied
 *      edit    -       -
 *      -       edit    -
 *
 * -------recover from pending-------
 * pre  -       -       pending
 *      -       -       cancelling
 *      edit    -       -
 *      -       edit    -
 *      -       -       canceled
 */
biz.userApplyCoupon = function * (json) {
  //console.log('userApplyCoupon ', json);
  /*
   json = {
   "cvid": 1001,
   "uid": 100,
   "qty": 11,
   "cond": "aa"
   }
   */

  //try {
  var cvid = json.cvid;
  var qty = json.qty;

  /* step1 simple check */
  var couponVendor = yield daoCV.getByCvid(cvid);
  assert.equal(couponVendor.errors, null);
  if (couponVendor._doc.qtyLeft == 0) {
    console.log('left qty = 0');
    return {errcode: 1, errMsg: 'left qty = 0'};
  }
  if (couponVendor._doc.qtyLeft < qty) {
    console.log('request qty too much');
    return {errcode: 1, errMsg: 'request qty too much'};
  }

  /* step 0: prepare doc */
  var couponUser = yield daoCU.addBlank(json.uid, cvid);
  if (couponUser.errors) {
    // 如果此步骤失败,则事务还未init，无需回滚
    console.log('fail to apply at prepare');
    return {errcode: 1, errmsg: 'fail to apply at prepare'};
  }
  /* step 1: initial */
  var transCoupon = yield daoTC.add(cvid, couponUser.cuid, qty);
  if (transCoupon.errors) {
    // 如果此步骤失败,则事务还未initial，无需回滚
    console.log('fail to apply before initial');
    return {errcode: 1, errmsg: 'fail to apply before initial'};
  }

  var cuid = couponUser._doc.cuid;
  var tid = transCoupon._doc.tid;

  /* step 2: initial => pending */
  transCoupon.state = 'pending';
  yield transCoupon.save();
  if (transCoupon.errors) {
    // 如果此步骤失败,则事务还未开始，回滚非常简单:删除此transCoupon
    console.log('fail to apply after initial');
    yield daoTC.remove(tid);
    return {errcode: 1, errmsg: 'fail to apply after initial'};
  }

  /* step 3: pending => applied */
  couponVendor = yield daoCV.transPhase_pending(cvid, json.qty, tid);
  if (!couponVendor) { // 通常是数量不足
    console.log('fail to apply at pending=>applied 1-1');
    yield biz.rollback(tid);
    return {errcode: 1, errmsg: 'fail to apply at pending=>applied 1-1'};
  }
  if (couponVendor.errors) {
    console.log('fail to apply at pending=>applied 1-2');
    yield biz.rollback(tid);
    return {errcode: 1, errmsg: 'fail to apply at pending=>applied 1-2'};
  }

  couponUser = yield daoCU.transPhase_pending(cuid, json.qty, tid);
  if (couponUser.errors) {
    console.log('fail to apply at pending=>applied 2');
    yield biz.rollback(tid);
    return {errcode: 1, errmsg: 'fail to apply at pending=>applied 2'};
  }

  transCoupon.state = 'applied';
  yield transCoupon.save();

  /* step 4: applied => done, applied之后,不允许rollback */
  couponVendor = yield daoCV.transPhase_applied(cvid, tid);
  couponUser = yield daoCU.transPhase_applied(cuid, tid);
  transCoupon.state = 'done';
  yield transCoupon.save();

  return {errcode: 0};
  //} catch (e) {
  //  console.log(e);
  //  return {errcode: 1, errmsg: 'fail to apply:' + e}
  //}
};

/**
 * pending 回滚
 * @params tid 事务id,如果没有,则查询所有pending 并回滚
 */
biz.rollback = function *(tid) {
  console.log('rollback tid=%s', tid);
  var t = yield daoTC.findOne({tid: tid, state: 'pending'});
  if (!t._doc) return;
  t.state = 'canceling';
  yield t.save();

  var cvid = t._doc.cvid;
  var cuid = t._doc.cuid;
  var qty = t._doc.qty;

  var cv = yield daoCV.findOneAndUpdate(
    {cvid: cvid, pendingTransactions: {$all: tid}}, /* query */
    {
      $inc: {qtyLeft: qty},
      $pull: {pendingTransactions: tid}
    },
    {upsert: false, 'new': true} /* option */
  );

  var cu = yield daoCV.findOneAndUpdate(
    {cuid: cuid, pendingTransactions: {$all: tid}}, /* query */
    {
      $inc: {qty: -qty},
      $pull: {pendingTransactions: tid}
    },
    {upsert: false, 'new': true} /* option */
  );
  // 在这个业务逻辑中，移除该couponUser
  yield daoCU.remove({cuid: cuid});

  t.state = 'canceled';
  yield t.save();
  console.log('rollback success, tid=%s', tid);
};

biz.rollbackAll = function *() {
  console.log('rollbackAll');
  var docs, i;
  // roll back all pending transaction
  var t_pending = yield daoTC.find({state: 'pending'});
  docs = t_pending._doc;
  if (docs) {
    for (i = 0; i < docs.length; i++) {
      yield biz.rollback(docs[i].tid);
    }
  }

  // roll back all canceling transaction
  var t_canceling = yield daoTC.find({state: 'pending'});
  docs = t_canceling._doc;
  if (docs) {
    for (i = 0; i < docs.length; i++) {
      yield biz.rollback(docs[i].tid);
    }
  }
};

/**
 * applied => done
 * @params tid 事务id,如果没有,则查询所有applied 并置为done
 */
biz.forceApply = function*(tid) {
  console.log('rollbackAll tid=%s', tid);
  var t = yield daoTC.findOne({tid: tid, state: 'applied'});
  if (!t._doc) return;
  var cvid = t._doc.cvid;
  var cuid = t._doc.cvid;
  var couponVendor = yield daoCV.transPhase_applied(cvid, tid);
  var couponUser = yield daoCU.transPhase_applied(cuid, tid);
  t.state = 'done';
  yield t.save();
};

biz.forceApplyAll = function*() {
  console.log('rollbackAll');
  // TODO
};
